/*
 * Copyright (c) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <poll.h>

#include "uart.h"

/*
 * 串口设置
 * Serial port settings
 */
int Uart1Config(int fd)
{
    struct termios newtio = {0}, oldtio = {0};
    /*
     * 获取原有串口配置
     * Get the original serial port configuration
     */
    if (tcgetattr(fd, &oldtio) != 0) {
        perror("SetupSerial 1");
        return -1;
    }
    (void)memset_s(&newtio, sizeof(newtio), 0, sizeof(newtio));
    /*
     * CREAD开启串行数据接收，CLOCAL打开本地连接模式
     * CREAD opens serial data reception, CLOCAL opens local connection mode
     */
    newtio.c_cflag  |=  CLOCAL | CREAD;

    /*
     * 设置数据位
     * set data bit
     */
    newtio.c_cflag &= ~CSIZE;
    newtio.c_cflag |= CS8;
    /*
     * 设置奇偶校验位
     * set parity bit
     */
    newtio.c_cflag &= ~PARENB; // 无奇偶校验
    /*
     * 设置波特率 115200
     * set baud rate 115200
     */
    cfsetispeed(&newtio, B115200);
    cfsetospeed(&newtio, B115200);

    /*
     * 设置停止位
     * set stop bit
     */
    newtio.c_cflag &=  ~CSTOPB; /* 默认为一位停止位 */
    /*
     * 设置最少字符和等待时间，对于接收字符和等待时间没有特别的要求时
     *
     * Set the minimum characters and waiting time,
     * when there are no special requirements for receiving characters and waiting time
     */
    newtio.c_cc[VTIME]  = 0; /* 非规范模式读取时的超时时间 */
    newtio.c_cc[VMIN] = 0; /* 非规范模式读取时的最小字符数 */
    /*
     * tcflush清空终端未完成的输入/输出请求及数据；TCIFLUSH表示清空正收到的数据，且不读取出来
     *
     * tcflush clears the unfinished input/output requests and data of the terminal;
     * TCIFLUSH means clearing the data being received and not reading it out
     */
    tcflush(fd, TCIFLUSH);
    if ((tcsetattr(fd, TCSANOW, &newtio)) != 0) {
        perror("com set error");
        return -1;
    }
    return 0;
}

/*
 * @berf uart 发送数据
 * @param int fd: 串口文件描述符
 * @param void *buf:发送数据buffer
 * @param int len:数据缓冲长度
 *
 * @berf uart send
 * @param int fd: uart file descriptor
 * @param void *buf:send data buf
 * @param int len:data buf len
 */
int UartSend(int fd, char *buf, int len)
{
    int ret = 0;
    int count = 0;
    char *sendBuf = buf;
    int sendLen = len;

    tcflush(fd, TCIFLUSH);

    while (sendLen > 0) {
        ret = write(fd, (char*)sendBuf + count, sendLen);
        if (ret < 1) {
            printf("write data below 1 byte % d\r\n", ret);
            break;
        }
        count += ret;
        sendLen = sendLen - ret;
    }

    return count;
}

/*
 * @berf uart 读取数据
 * @param int uart_fd: 串口文件描述符
 * @param void *buf: 读取数据buffer
 * @param int len: 数据缓冲区长度
 * @param int timeoutMs: 读取数据时间
 *
 * @berf uart read
 * @param int uart_fd: uart file descriptor
 * @param void *buf: read data buf
 * @param int len: data buf len
 * @param int timeoutMs: read data time
 */
int UartRead(int uartFd, char *buf, int len, int timeoutMs)
{
    int ret = 0;
    size_t  rsum = 0;
    ret = 0;
    fd_set rset;
    struct timeval time;
    int timeout = timeoutMs;
    char *readBuf = buf;
    int readLen = len;

    while (rsum < readLen) {
        time.tv_sec = timeout / 1000; /* 1000:转换成秒 */
        time.tv_usec = (timeout - time.tv_sec * 1000) * 1000; /* 1000, 1000:转换为微秒 */
        FD_ZERO(&rset);
        FD_SET(uartFd, &rset);
        ret = select(uartFd + 1, &rset, NULL, NULL, &time);
        if (ret <= 0) {
            if (ret == 0) {
                // printf("time over!\r\n");
                return -1;
            }
            if (ret == -1) {
                printf("select error!\r\n");
                continue; // 信号中断
            }
            return -1;
        } else {
            ret = read(uartFd, (char *)readBuf + rsum, readLen - rsum);
            if (ret < 0) {
                printf("read data failed\r\n");
                return ret;
            } else {
                rsum += ret;
            }
        }
    }
    return rsum;
}

/*
 * @berf CRC校验
 * @param const unsigned char *buf: 待校验数据buff
 * @param unsigned int size: 待校验数据长度
 *
 * @berf CRC check
 * @param const unsigned char *buf: Data to be verified buff
 * @param unsigned int size: Data to be verified length
 */
unsigned short crc16(const unsigned char *buf, unsigned int size)
{
    unsigned int  i;
    unsigned short crc = 0x0000;

    for (i = 0; i < size; i++) {
        crc += buf[i];
    }
    return crc & 0xFFFF;
}

/*
 * 对数据进行打包
 * Pack the data
 */
static unsigned int HisignallingDataPackage(HisignallingProtocalType *buf,
    unsigned int len, unsigned char *hisignallingDataBuf)
{
    unsigned short crc = 0x0000;
    unsigned int packageLen = 0;
    unsigned int DataPackLen = len;

    (void)memcpy_s(hisignallingDataBuf, HISGNALLING_MSG_FRAME_HEADER_LEN,
        buf->frameHeader, HISGNALLING_MSG_FRAME_HEADER_LEN);
    (void)memcpy_s(&hisignallingDataBuf[HISGNALLING_MSG_FRAME_HEADER_LEN],
        1, &(buf->frameType), 1);
    (void)memcpy_s(&hisignallingDataBuf[HISGNALLING_MSG_FRAME_HEADER_LEN + 1],
        1, &(buf->dataLen), 1);
    (void)memcpy_s(&hisignallingDataBuf[HISIGNALLING_MSG_HEADER_TAIL_LEN],
        DataPackLen, buf->hisignallingMsgBuf, DataPackLen);
    crc = crc16(hisignallingDataBuf, (DataPackLen + HISIGNALLING_MSG_HEADER_TAIL_LEN));

    hisignallingDataBuf[DataPackLen + HISIGNALLING_MSG_HEADER_TAIL_LEN] =
        (unsigned char)((crc & 0xff00) >> RIGHT_MOVE_8_BIT);
    hisignallingDataBuf[DataPackLen + HISIGNALLING_MSG_HEADER_TAIL_LEN + 1] =
        (unsigned char)(crc & 0x00ff);

    packageLen = DataPackLen + HISIGNALLING_MSG_HEADER_TAIL_LEN + 2; /* 2: crc lenght */
    return packageLen;
}

/*
 * Hi3561DV300接收数据
 * Hi3561DV300 message recevice
 */
HisignallingErrorType HisignallingMsgReceive(int fd, unsigned char *buf, unsigned int len)
{
    int readLen = 0;
    /*
     * Hi3516dv300 uart串口读数据
     * Hi3516dv300 uart read data
     */
    readLen = UartRead(fd, buf, len, 1000); /* 1000 :time out */
    if (readLen <= 0) {
        // printf("uart_read data failed\r\n");
        return HISIGNALLING_RET_VAL_ERROR;
    }
    /*
     * 输出收到的数据
     * output received data
     */
    printf("recv >>>>\n");
    for (int i = 0; i < readLen; i++) {
        printf("0x%0x ", buf[i]);
    }
    printf("\n");
    return HISIGNALLING_RET_VAL_CORRECT;
}

/*
 * @berf hisignalling 协议发送消息
 * @param void *buf: 发送数据缓冲区
 * @param unsigned int data_len: 发送数据长度
 *
 * @berf hisignalling protocol send msg
 * @param void *buf: send data buff
 * @param unsigned int data_len: send data length
 */
unsigned int HisignallingMsgSend(int fd, char *buf, unsigned int dataLen, refuseClassification frameType)
{
    unsigned int ret = 0;
    HisignallingProtocalType hisignallingMsg = {0};
    unsigned char hisignallingSendBuf[HISIGNALLING_MSG_BUFF_LEN] = {0};
    unsigned int hisignallingPackageLen = 0;
    unsigned int writeDataLen = 0;

    hisignallingMsg.frameHeader[0]= HISIGNALLING_HEAD_1; /* Protocol head data 1 */
    hisignallingMsg.frameHeader[1]= HISIGNALLING_HEAD_2; /* Protocol head data 2 */
    hisignallingMsg.frameType = frameType;
    hisignallingMsg.dataLen = (unsigned char)(dataLen & 0xFF);
    (void)memcpy_s(hisignallingMsg.hisignallingMsgBuf, dataLen, buf, dataLen);

    hisignallingPackageLen = HisignallingDataPackage(&hisignallingMsg, dataLen, hisignallingSendBuf);
    if (!hisignallingPackageLen) {
        printf("hisignalling_data_package failed\r\n");
        return -1;
    }
    if (*hisignallingSendBuf == 0) {
        printf("hisignalling send buf is null!\r\n");
        return -1;
    }

    ret = UartSend(fd, hisignallingSendBuf, HISIGNALLING_MSG_ONE_FRAME_LEN);
    if (ret < 0) {
        printf("write data failed\r\n");
        return -1;
    }

    return 0;
}

unsigned int UartOpenInit(void)
{
    int fd;
    char *uart1 = "/dev/ttyAMA1";

    if ((fd = open(uart1, O_RDWR | O_NOCTTY | O_NDELAY)) < 0) {
        printf("open %s is failed", uart1);
        return -1;
    } else {
        Uart1Config(fd);
    }
    return fd;
}
